Project 1: iGoogle
Nii Okai Addy and Tayo Oguntebi :CS349W Project 1 iGoogle Drag-and-Drop: Structure of the Interaction iGoogle is a personalized home page that allows user customization through the addition of various widgets. These widgets serve many different purposes, from checking e-mail and weather, to displaying up-to-date news headlines. The arrangement and placement of the widgets on a user's home page is accomplished via a smooth AJAX-based drag-and-drop interface. A user simply clicks the header (or title bar) of a widget and begins to drag. When this happens, the page dynamically updates to show the user how the page would look if he/she were to release the mouse at that given instant. The layout of the page is initially three columns of widgets. Any widget can be clicked and dragged from any column to any other column and placed above or below any other widget. This document will focus on the semantics underlying the dynamic relocation of widgets on the screen during a click-and-drag event. The basic structure of the HTML on iGoogle's page is as follows: div id="t_1" div id="c_1" div id="m_1"> div id="m_2"> div id="c_2" div id="m_3"> div id="c_3" div id="m_4"> div id="m_5"> The 't_*' elements correspond to tabs on the iGoogle page. The 'c_*' elements correspond to columns on the iGoogle page. The 'm_*' elements correspond to individual modboxes (widgets) on the page. Javascript Setup and Event Handlers Although the page is initially laid out using HTML, iGoogle makes frequent use of Javascript event handlers to set up and add interactivity to the web page. Upon loading the page, a significant amount of Javascript code is used to attach functions and properties to each module. For example, the various event handlers (onmousedown, etc.) for the different elements are dynamically attached when the page is loaded. Drag-and-Drop Calculations The drag-and-drop effect is accomplished through calculations in Javascript in accordance with a series of event handlers. A step-by-step breakdown of the function calls is detailed below in the 'How it Works' section. Browser-Server Communication The iGoogle home page is not an extremely data-intensive site by nature. As such, the vast majority of computation and data handling is done on the client side, and there is not great need for browser-server communication apart from the initial fetching of the HTML and scripts. Although there is not too much asynchronous server to browser communication, there needs to be a fair amount of browser to server communication. iGoogle serves as a homepage which should remember the content and the settings for all users. As such, iGoogle uses AJAX techniques via an XMLHttpRequest object. This object is used to send state data to the server for saving. The rationale for doing this asynchronously is to cover the case where the system unexpectedly goes down -- one would want his/her data to be stored and updated on the server in such an event. One unique feature of iGoogle is that many of the informational widgets are populated using RSS feeds. The updating of these widgets is then completely up to the servers which are distributing the content. This is not a problem because of the fact that the RSS update is a relatively rare event. Questions about Web Limitations and Possible Improvements Did any facilities of the Web make this interaction easy or hard? *Hard **Browser area size vs iGoogle screen size ***Difficulty arises when using the mouse in a window which is much smaller than the desired iGoogle screen size. Checks must be performed to account for the case when the mouse exits the browser area on the screen. *Easy **Relative re-positioning and style sheets ***When using style sheets, as long as modules (widgets) are the child of a column element, they are correctly placed in the column. When moving a module to a new location, this can then be achieved with a simple command. There is no need to calculate new heights and absolute positions of all the modules when only one module moves. ***Example: ****We would like to move module b'' below module ''a where a.parentnode is the column containing a''. ****The code is simply: a.parentNode.insertBefore(b, a.nextSibling); Are there any improvements to the Web that would have made this easier or more powerful? A higher-level abstraction more amenable to graphical interaction of widgets would have been preferable (for example, a toolkit). Javascript is not optimal here. For example, the function which calculates the distance of a module to other modules while dragging is implemented as a for-loop that loops over all modules and calculates the distance to these modules as well as displacements, etc. The calculation uses the sqrt(x^2 + y^2) equation to calculate distance. A nicer abstraction would offer a function that tells how far a widget is from another and perhaps even trigger an event if any widget comes within N pixels of another widget. A toolkit or other layer which could perhaps produce the Javascript code would have been helpful, although it is not clear if Google engineers used such tools to write this Javascript. The Google Web Toolkit (GWT) provides some abstractions that could make such an interaction easier. Are the techniques used in this interaction relevant for a broader class of interactions? This interaction boils down to 2 components. One part is click-and-drag while another is the repositioning 'div' elements. The techniques used for the drag-and-drop portion are somewhat common and already used in a wide range of applications. The positioning and displacement techniques are more unique but are still found in many websites although in a slightly different form. Any site that has some form of collapsible menu which moves other parts of the page when opened is most likely using the same method for repositioning which is determined by the style sheet. How it Works Setup *_IG_initDrag called with inputs ''"t_1" and "tabs" **The function goes through all the modules id=''"m_#"'' adding functions and properties which include ***''_dragStart'' function ***''_drag'' function ***''_dragEnd'' function ***''onmousedown'' event which calls _dragStart ***''onmousemove'' event which calls _drag ***''onmouseup'' event which calls _dragEnd '' Clicking *Begin processing when the header ''"id="m_#_h" within the module id="m_#" is clicked *Get mouse position using b.lastMouseX=a.clientX; b.lastMouseY=a.clientY; **''a'' is the event **''b'' is the selected module *Get position of top left corner of the clicked module using b.style.left b.style.top *Store original next sibling of the module which corresponds to the widget below the selected widget on the display this.origNextSibling=this.a.nextSibling; **''this.a'' is the selected module *Create a module holder, which is a box the same size and in the position of the clicked module's beginning position. *Shift clicked module to the right to signify that it has been selected *Use setInterval to set a function to check if the mouse is within the browser screen area every 10ms Dragging *Adjust the opacity of module being dragged to make it semi-transparent as it moves around the screen using this.a.style.filter="alpha(opacity=50)"; this.a.style.opacity=0.5; *Get the mouse position *Check to see if the mouse is still being clicked and end the drag if it is not *Use mouse position to update position of the selected module *Calculate the distance of top left corner of the selected module to that of all the other modules using var h = Math.sqrt(Math.pow(g-f.a.pagePosLeft,2)+Math.pow(b-f.a.pagePosTop,2)); **''b'' is the coordinate of the top position of the selected module **''g'' is the coordinate of the left position of the selected module **''f.a'' is another module *Store the closest module to the selected module *If not already there insert the dashed modbox in the column of the closest module before that module using c.a.parentNode.insertBefore(k,c.a); **''c'' is an object containing a module **''c.a'' is the closest module to the selected module **''k'' is the module placeholder *If the mouse goes off the top of the browser screen, float the module off the top of the browser Ending the Drag *Remove data from the selected module this.a.style.position=""; this.a.style.width=""; this.a.style.zIndex=""; this.a.style.filter=""; this.a.style.opacity=""; *Move the module to the position where the placeholder was using b.parentNode.insertBefore(this.a,b.nextSibling) **''b'' is the placeholder object **''b.parentNode'' is a column **''this.a'' is the selected module *Remove the placeholder using b.parentNode.removeChild(b) *Remove events created with initial click by setting the events to null *Stop checking if module is off the screen using clearInterval Interesting Challenges Automatic Code Generation and Textual Compression The iGoogle Javascript code is written to a dynamically created file and is compressed textually to minimize size. Most of the variables used in the code are a,b,c,d and so on as can be seen in the above examples. Many of the function names are in the format ig_xxx, where xxx is some sequence of characters. This made it difficult to keep track of functions and understand their roles. Javascript apply method The apply method is used in javascript to replace the this variable when a function is called. This also made the code difficult to parse because many functions ended up having properties which pointed at each other.